﻿using System;
using System.Diagnostics;
using Network;
using Model.Nova;
using Model;
using Providers.Nova.Modules;
using Network.Utilities;
using System.Threading.Tasks;
using Network.Messages.Nova;
using Model.Extensions;
using Providers.Nova.Server;

namespace Managers.Nova.Server
{
    public sealed class NovaManager : Manager<NovaProvider>
    {
        private static readonly long s_timeInitialized = Stopwatch.GetTimestamp();
        private static readonly double s_dInvFreq = 1.0 / (double)Stopwatch.Frequency;

        /// <summary>
        /// Get number of seconds since the application started
        /// </summary>
        public static double Now { get { return (double)(Stopwatch.GetTimestamp() - s_timeInitialized) * s_dInvFreq; } }

        public NovaManager(NetworkPeer network)
            : base(new NovaProvider(network))
        {
            Provider.OnIntroducerRegistrationResponded += (s, e) =>
            {
                OnIntroducerRegistrationResponded?.Invoke(s, e);
                m_NextAllowRegistTime = (float) Now +5f;
            };
            Provider.OnRequestNewPassword += Provider_OnRequestNewPassword;
            Provider.OnConnectionLosted += Provider_OnConnectionLosted;
            Provider.OnAskServerShutdownMessage += Provider_OnAskServerShutdownMessage;
        }

        private void Provider_OnAskServerShutdownMessage(object sender, AskServerShutdownMessage e)
        {
            OnAskServerShutdownMessage?.Invoke(this, e);
        }

        private float m_NextAllowRegistTime = (float)Now;

        private void Provider_OnRequestNewPassword(object sender, EventArgs e)
        {
            Provider.ServerMachine.Password = AlphaNumericGenerator.Generate(3, GeneratorOptions.AlphaNumeric);
            Provider.ServerMachine.PasswordHash = HashUtil.ComputeHash(Provider.ServerMachine.Password, HashType.SHA512);

            var newPasswordEventArgs = new PasswordGeneratedEventArgs { NewPassword = Provider.ServerMachine.Password };

            //// 通知自己的界面更新
            //OnNewPasswordGenerated?.Invoke(this, newPasswordEventArgs);
            // 通知客户端更新
            Provider.NotifyClientUserInfoChanged();
            // 通知中介进行更新
            RegisterWithIntroducerAsTask();
        }

        public async Task<PasswordGeneratedEventArgs> GeneratePassword()
        {
            return await GeneratePasswordAsTask();
        }

        public Task<PasswordGeneratedEventArgs> GeneratePasswordAsTask()
        {
            Trace.WriteLine("Ran");
            var tcs = RegisterAsTask<PasswordGeneratedEventArgs>(ref OnNewPasswordGenerated);

            var childTask = Task<PasswordGeneratedEventArgs>.Factory.StartNew(() =>
            {
                Provider.ServerMachine.Identity = MachineIdentity.GetCurrentIdentity();
                Provider.ServerMachine.Password = AlphaNumericGenerator.Generate(3, GeneratorOptions.AlphaNumeric);
                Provider.ServerMachine.PasswordHash = HashUtil.ComputeHash(Provider.ServerMachine.Password, HashType.SHA512);

                var newPasswordEventArgs = new PasswordGeneratedEventArgs { NewPassword = Provider.ServerMachine.Password };
                OnNewPasswordGenerated?.Invoke(this, newPasswordEventArgs);

                return newPasswordEventArgs;
            }, TaskCreationOptions.AttachedToParent);

            return tcs.Task;
        }

        private bool _flagRegisted = false;
        public Task<IntroducerRegistrationResponsedEventArgs> RegisterWithIntroducerAsTask()
        {
            var tcs = RegisterAsTask(ref OnIntroducerRegistrationResponded);
            
            // We create a new machine instead of sending ServerMachine because ServerMachine has the plain text password and is a local copy only
            var serverMachine = new Machine
            {
                Identity = Provider.ServerMachine.Identity,
                PasswordHash = Provider.ServerMachine.PasswordHash,
                PrivateEndPoint = Network.GetInternalEndPoint()
            };
            
            Network.SendUnconnectedMessage(new RequestIntroducerRegistrationMessage { Machine = serverMachine }, Config.GetIPEndPoint("IntroducerEndPoint"));
            _flagRegisted = true;
            return tcs.Task;
        }

        /// <summary>
        /// 如果之前注册，说明连接真的断掉了，那么在时间允许的情况下重连吧
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Provider_OnConnectionLosted(object sender, EventArgs e)
        {
            if (_flagRegisted && m_NextAllowRegistTime < Now)
            {
                RegisterWithIntroducerAsTask();
                m_NextAllowRegistTime = (float)Now + 5f;
            }
        }

        /// <summary>
        /// 发送在线包
        /// </summary>
        public void SendKeepAliveMessage()
        {
           Network.SendUnconnectedMessage(new KeepAliveMessage(), Config.GetIPEndPoint("IntroducerEndPoint"));
        }

        public event EventHandler<PasswordGeneratedEventArgs> OnNewPasswordGenerated;
        public event EventHandler<IntroducerRegistrationResponsedEventArgs> OnIntroducerRegistrationResponded;
        public event EventHandler<AskServerShutdownMessage> OnAskServerShutdownMessage;

        public void Stop()
        {
            
            Network.Shutdown();
            
        }
    }
}
